
#include <JSL.hpp>

#include "../debug.hpp"
#include "../math.hpp"
#include "../gl.hpp"
#include "../utils/JSLConvert.hpp"
#include "Enabling.hpp"
#include "TextureManager.hpp"

#include "Material.hpp"

namespace zen {

	namespace video {

		Material::Material() {
			color = glm::vec4(1.0, 1.0, 1.0, 1.0);
			ambient = glm::vec4(0.2, 0.2, 0.2, 1.0);
			diffuse = glm::vec4(0.8, 0.8, 0.8, 1.0);
			specular = glm::vec4(0.0, 0.0, 0.0, 1.0);
			emission = glm::vec4(0.0, 0.0, 0.0, 1.0);
			shininess = 0.0;
		}

		Material::Material(const Material &other) {
			*this = other;
		}

		Material::Material(const std::string &name, const glm::vec4 &ambient, const glm::vec4 &diffuse, const glm::vec4 &specular, const glm::vec4 &emission, float shininess, const PTexture &texture) {
			this->name = name;
			this->color = color;
			this->ambient = ambient;
			this->diffuse = diffuse;
			this->specular = specular;
			this->emission = emission;
			this->shininess = shininess;
			this->texture = texture;
		}

		Material& Material::operator = (const Material &other) {
			name = other.name;
			texture = other.texture;
			shininess = other.shininess;
			color = other.color;
			ambient = other.ambient;
			diffuse = other.diffuse;
			specular = other.specular;
			emission = other.emission;
			return *this;
		}

		void Material::activate(GLenum face) const {
			glColor4fv(glm::value_ptr(color));
			glMaterialfv(face, GL_AMBIENT, glm::value_ptr(ambient));
			glMaterialfv(face, GL_DIFFUSE, glm::value_ptr(diffuse));
			glMaterialfv(face, GL_SPECULAR, glm::value_ptr(specular));
			glMaterialfv(face, GL_EMISSION, glm::value_ptr(emission));
			glMaterialf(face, GL_SHININESS, shininess);
			if (texture->getHandle() == 0) {
				Disable::texture2D();
			} else {
				Enable::texture2D();
				glBindTexture(GL_TEXTURE_2D, texture->getHandle());
			}
		}

		bool Material::loadResource(const jsl::Node *node, TextureManager* textureManager) {
			zen_call("Material.loadResource");
			if (node == NULL) {
				zen_log("Error: Invalid node, must not be null.");
				return false;
			}
			try {
				name = node->at("name")->value();
				shininess = node->at("shininess")->asFloat();
				zen::utils::JSLConvert::load(color, node->at("color"), glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
				zen::utils::JSLConvert::load(ambient, node->at("ambient"));
				zen::utils::JSLConvert::load(diffuse, node->at("diffuse"));
				zen::utils::JSLConvert::load(specular, node->at("specular"));
				zen::utils::JSLConvert::load(emission, node->at("emission"));
				color /= 256.0;
				ambient /= 256.0;
				diffuse /= 256.0;
				specular /= 256.0;
				emission /= 256.0;
				jsl::Node* tmp = node->at("texture");
				if (tmp->asString() != "")
					texture = textureManager->loadFile(tmp->asString());
			} catch (const std::exception &e) {
				zen_log("Error: " + std::string(e.what()));
				return false;
			}
			return true;
		}

		bool Material::saveResource(jsl::Node *node) {
			zen_call("Material.saveResource");
			if (node == NULL) {
				zen_log("Error: Invalid node, must not be null.");
				return false;
			}
			try {
        		node->set("name", name);
				node->set("texture", texture->empty() ? "" : texture->getFileName());
				node->set("shininess", shininess);
				zen::utils::JSLConvert::save(node->path("color", true), color * 256.0);
				zen::utils::JSLConvert::save(node->path("ambient", true), ambient * 256.0);
				zen::utils::JSLConvert::save(node->path("diffuse", true), diffuse * 256.0);
				zen::utils::JSLConvert::save(node->path("specular", true), specular * 256.0);
				zen::utils::JSLConvert::save(node->path("emission", true), emission * 256.0);
			} catch (const std::exception &e) {
				zen_log("Error: " + std::string(e.what()));
				return false;
			}
			return true;
		}


	} // namespace video

} // namespace zen
